using GamFi.API.Application.DefaultSettings;
using GamFi.API.Application.Interfaces;
using GamFi.API.Domain.Entities;
using Microsoft.EntityFrameworkCore;

namespace GamFi.API.Persistence;

public class DbSeeder : IDbSeeder
{
    private static readonly IReadOnlyDictionary<Guid, string> BaseRoles = new Dictionary<Guid, string>()
    {
        [Application.DefaultSettings.BaseRoles.AdminId] =
            Application.DefaultSettings.BaseRoles.AdminRoleName,

        [Application.DefaultSettings.BaseRoles.UserId] =
            Application.DefaultSettings.BaseRoles.UserRoleName,

        [Application.DefaultSettings.BaseRoles.CreatorId] =
            Application.DefaultSettings.BaseRoles.CreatorRoleName
    };

    /// <inheritdoc />
    public async Task SeedAsync(IDbContext efContext, CancellationToken cancellationToken)
    {
        await SeedBaseCountriesAsync(efContext, cancellationToken);
        await SeedBaseRolesAsync(efContext, cancellationToken);
        await SeedBaseImagesAsync(efContext, cancellationToken);
        await SeedBaseChainsAsync(efContext, cancellationToken);
        await efContext.SaveChangesAsync(cancellationToken);
    }

    private static async Task SeedBaseCountriesAsync(IDbContext dbContext, CancellationToken cancellationToken)
    {
        var existingCountryNames = await dbContext.Countries
            .Select(x => x.Name)
            .ToListAsync(cancellationToken);

        var countriesToSeed = BaseCountries.AllBaseCountries
            .Where(x => !existingCountryNames.Contains(x.Key))
            .Select(x => new Country
            {
                Name = x.Key,
                Code = x.Value.Code,
                AlphaTwo = x.Value.AlphaTwo,
                AlphaThree = x.Value.AlphaThree
            })
            .ToList();

        await dbContext.Countries.AddRangeAsync(countriesToSeed, cancellationToken);
    }

    private static async Task SeedBaseRolesAsync(IDbContext dbContext, CancellationToken cancellationToken)
    {
        var existsRolesInDb = await dbContext.Roles
            .Where(x => BaseRoles.Keys.Contains(x.Id))
            .Select(x => x.Id)
            .ToListAsync(cancellationToken);

        var rolesToSeed = BaseRoles
            .Where(x => !existsRolesInDb.Contains(x.Key))
            .Select(x => new Role { Id = x.Key, Name = x.Value, NormalizedName = x.Value.ToUpper() })
            .ToList();

        await dbContext.Roles.AddRangeAsync(rolesToSeed, cancellationToken);
    }

    private static async Task SeedBaseImagesAsync(IDbContext dbContext, CancellationToken cancellationToken)
    {
        var existingImageIds = await dbContext.Images
            .Select(x => x.Id)
            .ToListAsync(cancellationToken);

        var imagesToSeed = BaseImages.AllBaseImages
            .Where(x => !existingImageIds.Contains(x.Key))
            .Select(x => new Image
            {
                Id = x.Key,
                Address = x.Value.Address,
                Size = x.Value.Size,
                ImageName = x.Value.ImageName,
                CreatedDate = x.Value.CreatedDate
            })
            .ToList();

        await dbContext.Images.AddRangeAsync(imagesToSeed, cancellationToken);
    }

    private static async Task SeedBaseChainsAsync(IDbContext dbContext, CancellationToken cancellationToken)
    {
        var existingChainNames = await dbContext.Chains
            .Select(x => x.Name)
            .ToListAsync(cancellationToken);

        var chainsToSeed = BaseChains.AllBaseChains
            .Where(x => !existingChainNames.Contains(x.Key))
            .Select(x => new Chain
            {
                Name = x.Key,
                ImageId = x.Value
            })
            .ToList();

        await dbContext.Chains.AddRangeAsync(chainsToSeed, cancellationToken);
    }
}